﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.Office.Server.Search.Query;
using System.Text;
using System.Xml.Serialization;
using Microsoft.SharePoint;

namespace PS.WebParts.Rollup.Search.Core.Query
{
    public class FromStatement
    {
        // Methods
        public FromStatement() { }
        public override string ToString()
        {
            return " FROM portal..scope() ";
        }

    }

    public enum GroupLogicalOperators
    {
        AND = 2,
        OR = 1
    }

    public enum LogicalOperators
    {
        AND = 2,
        ANDNOT = 3,
        OR = 1,
        ORNOT = 4
    }

    public class MOSSSQLSearch
    {
        // Fields
        private FromStatement fromStatement = new FromStatement();
        private SelectStatement selectStatement;
        private SortStatement sortStatement = new SortStatement();
        private WhereStatement where;

        // Methods

        private List<WhereGroup> expandMembership(SPWeb web)
        {
            List<WhereGroup> newGroups = new List<WhereGroup>();

            List<WhereProperty> membershipWhere = new List<WhereProperty>();

            List<string> usermembership = new List<string>();

            foreach (WhereGroup whereGroup in this.where.WhereGroups)
            {
                WhereGroup copy = new WhereGroup();
                copy.LogOperator = whereGroup.LogOperator;

                foreach (var prop in whereGroup.WhereProperties)
                {
                    if (prop is WhereMembershipContains)
                    {
                        WhereMembershipContains membershipProp = (WhereMembershipContains)prop;

                        if (web.CurrentUser.IsSiteAdmin && membershipProp.IgnoreForSiteAdmin) continue;

                        WhereSubGroup wmC = new WhereSubGroup();

                        wmC.PropertyOperator = prop.PropertyOperator;

                        if (usermembership.Count == 0)
                            usermembership = GetUserMembership(web);

                        GetUserMembership(web, usermembership, membershipProp)
                            .ForEach(a => wmC.WhereProperties.Add(a));

                        if (wmC.WhereProperties.Count > 0)
                        {
                            wmC.WhereProperties[0].PropertyOperator = null;
                            copy.WhereProperties.Add(wmC);
                        }
                    }
                    else
                    {
                        copy.WhereProperties.Add(prop);
                    }
                }

                newGroups.Add(copy);
            }

            return newGroups;
        }

        public string ToString(SPWeb web)
        {
            StringBuilder builder = new StringBuilder();

            builder.Append(this.selectStatement.ToString());

            builder.Append(this.fromStatement.ToString());
            
            List<WhereGroup> expandedWhereGroups = expandMembership(web);

            this.Where.WhereGroups = expandedWhereGroups;

            builder.Append(this.where.ToString(web));

            builder.Append(this.sortStatement.ToString());

            return builder.ToString();
        }

        List<WhereProperty> GetUserMembership(SPWeb web, List<string> usermembership, WhereMembershipContains memberWhere)
        {
            List<WhereProperty> result = new List<WhereProperty>();

            foreach (string bit in usermembership)
            {
                WherePropertyContains wProp = new WherePropertyContains();
                wProp.Property = memberWhere.Property;
                wProp.SearchTerm = bit;
                wProp.PropertyOperator = new LogicalOperators?(LogicalOperators.OR);

                result.Add(wProp);
            }

            return result;
        }

        List<string> GetUserMembership(SPWeb web)
        {
            List<string> result = GetUserSPGroups(web);

            result.Add(web.CurrentUser.Name);

            return result;
        }

        List<string> GetUserSPGroups(SPWeb web)
        {
            List<string> result = new List<string>();

            foreach (SPGroup g in web.SiteGroups)
            {
                if (g.ContainsCurrentUser)
                    result.Add(g.Name);
            }

            return result;
        }

        public string ToString(bool formatted, SPWeb web)
        {
            StringBuilder builder = new StringBuilder();
            builder.AppendLine(this.selectStatement.ToString().Trim());
            builder.AppendLine(this.fromStatement.ToString().Trim());
            builder.AppendLine(this.where.ToString(web).Trim());
            builder.AppendLine(this.sortStatement.ToString().Trim());
            return builder.ToString();
        }

        // Properties
        [XmlIgnore]
        public FromStatement FromStatement
        {
            get
            {
                return this.fromStatement;
            }
            set
            {
                this.fromStatement = value;
            }
        }

        public SelectStatement SelectStatement
        {
            get
            {
                return this.selectStatement;
            }
            set
            {
                this.selectStatement = value;
            }
        }

        public SortStatement SortStatement
        {
            get
            {
                return this.sortStatement;
            }
            set
            {
                this.sortStatement = value;
            }
        }

        public WhereStatement Where
        {
            get
            {
                return this.where;
            }
            set
            {
                this.where = value;
            }
        }
    }

    public enum SearchTypes
    {
        Contains = 1,
        FreeText = 2
    }

    public class SearchWherePropertyDateAdd : WhereProperty
    {
        // Fields
        private int within;
        private TimeUnits withinTimeUnit;

        // Methods
        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();
            builder.Append("(");
            builder.Append(base.Property);
            builder.Append(" >=DATEADD(");
            builder.Append(this.withinTimeUnit.ToString());
            builder.Append(", -");
            builder.Append(this.within);
            builder.Append(", GETGMTDATE()))");
            return builder.ToString();
        }

        // Properties
        public int Within
        {
            get
            {
                return this.within;
            }
            set
            {
                this.within = value;
            }
        }

        public TimeUnits WithinTimeUnit
        {
            get
            {
                return this.withinTimeUnit;
            }
            set
            {
                this.withinTimeUnit = value;
            }
        }

    }

    public class SelectStatement
    {
        // Fields
        private bool includeDefaultProperties = true;
        private List<string> returnProperties;

        // Methods
        public SelectStatement()
        {
            GetDefaultProperties();
        }

        internal static List<string> GetDefaultProperties()
        {
            List<string> list = new List<string>();
            list.Add("Title");
            list.Add("Rank");
            list.Add("Size");
            list.Add("Description");
            list.Add("Write");
            list.Add("Path");
            return list;
        }

        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();
            builder.Append("SELECT ");
            if (this.includeDefaultProperties)
            {
                foreach (string str in GetDefaultProperties())
                {
                    builder.Append(str);
                    builder.Append(", ");
                }
            }
            foreach (string str in this.returnProperties)
            {
                builder.Append(str);
                builder.Append(", ");
            }
            builder.Remove(builder.Length - 2, 2);
            return builder.ToString();
        }

        // Properties
        public bool IncludeDefaultProperties
        {
            get
            {
                return this.includeDefaultProperties;
            }
            set
            {
                this.includeDefaultProperties = value;
            }
        }

        public List<string> ReturnProperties
        {
            get
            {
                return this.returnProperties;
            }
            set
            {
                this.returnProperties = value;
            }
        }

    }

    public class SortStatement
    {
        // Fields
        private SortDirection resultSortOrder = SortDirection.Descending;
        private string sortProperty;

        // Methods
        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();
            builder.Append(" ORDER BY \"");
            if (string.IsNullOrEmpty(this.sortProperty))
            {
                builder.Append("Rank");
            }
            else
            {
                builder.Append(this.sortProperty);
            }
            builder.Append("\"");
            if (this.resultSortOrder == SortDirection.Ascending)
            {
                builder.Append(" ASC");
            }
            else
            {
                builder.Append(" DESC");
            }
            return builder.ToString();
        }

        // Properties
        public SortDirection ResultSortOrder
        {
            get
            {
                return this.resultSortOrder;
            }
            set
            {
                this.resultSortOrder = value;
            }
        }

        public string SortProperty
        {
            get
            {
                return this.sortProperty;
            }
            set
            {
                this.sortProperty = value;
            }
        }

    }

    public class TestClass
    {
        // Methods
        private void testfunction()
        {
            MOSSSQLSearch search = new MOSSSQLSearch();
            SelectStatement statement = new SelectStatement();
            List<string> list = new List<string>();
            List<string> list2 = new List<string>();
            list2.Add("All Sites");
            WhereStatement statement2 = new WhereStatement();
            statement2.Scopes = list2;
            statement2.Keywords = "test";
            statement2.KeywordsType = SearchTypes.Contains;
            WhereGroup group = new WhereGroup();
            WherePropertyContains item = new WherePropertyContains();
            item.Property = "Account";
            item.SearchTerm = "sdfsdf";
            group.WhereProperties.Add(item);
            item = new WherePropertyContains();
            item.Property = "contentclass";
            item.SearchTerm = "sdfsdf";
            item.PropertyOperator = LogicalOperators.AND;
            group.WhereProperties.Add(item);
            list.Add("Categories");
            search.Where = statement2;
            statement.ReturnProperties = list;
            search.SelectStatement = statement;
        }

    }

    public enum TimeUnits
    {
        YEAR,
        QUARTER,
        MONTH,
        WEEK,
        DAY,
        HOUR,
        MINUTE,
        SECOND
    }

    public class WhereGroup
    {
        // Fields
        private GroupLogicalOperators? logOperator;
        private List<WhereProperty> whereProperties = new List<WhereProperty>();

        // Methods
        private string GetPropertyString()
        {
            StringBuilder builder = new StringBuilder();

            builder.Append("(");

            foreach (WhereProperty property in this.whereProperties)
            {
                if (!property.PropertyOperator.HasValue)
                {
                    builder.Insert(1, property.ToString());
                }
                else
                {
                    builder.Append(property.ToString(true));
                }
            }

            builder.Append(")");

            return builder.ToString();
        }

        public override string ToString()
        {
            return this.GetPropertyString();
        }

        public string ToString(bool IncludeOperator)
        {
            string str = "";
            GroupLogicalOperators valueOrDefault = this.LogOperator.GetValueOrDefault();
            if (this.LogOperator.HasValue)
            {
                switch (valueOrDefault)
                {
                    case GroupLogicalOperators.OR:
                        str = " OR ";
                        goto Label_0048;

                    case GroupLogicalOperators.AND:
                        str = " AND ";
                        goto Label_0048;
                }
            }
            str = "";
        Label_0048:
            return (str + this.ToString());
        }

        // Properties
        public GroupLogicalOperators? LogOperator
        {
            get
            {
                return this.logOperator;
            }
            set
            {
                this.logOperator = value;
            }
        }

        public List<WhereProperty> WhereProperties
        {
            get
            {
                return this.whereProperties;
            }
            set
            {
                this.whereProperties = value;
            }
        }

    }

    public class WhereSubGroup : WhereProperty
    {
        // Fields
        private GroupLogicalOperators? logOperator;
        private List<WhereProperty> whereProperties = new List<WhereProperty>();

        // Methods
        private string GetPropertyString()
        {
            StringBuilder builder = new StringBuilder();

            builder.Append("(");

            foreach (WhereProperty property in this.whereProperties)
            {
                if (!property.PropertyOperator.HasValue)
                {
                    builder.Insert(1, property.ToString());
                }
                else
                {
                    builder.Append(property.ToString(true));
                }
            }

            builder.Append(")");

            return builder.ToString();
        }

        public override string ToString()
        {
            return this.GetPropertyString();
        }

        public string ToString(bool IncludeOperator)
        {
            string str = "";
            GroupLogicalOperators valueOrDefault = this.LogOperator.GetValueOrDefault();
            if (this.LogOperator.HasValue)
            {
                switch (valueOrDefault)
                {
                    case GroupLogicalOperators.OR:
                        str = " OR ";
                        goto Label_0048;

                    case GroupLogicalOperators.AND:
                        str = " AND ";
                        goto Label_0048;
                }
            }
            str = "";
        Label_0048:
            return (str + this.ToString());
        }

        // Properties
        public GroupLogicalOperators? LogOperator
        {
            get
            {
                return this.logOperator;
            }
            set
            {
                this.logOperator = value;
            }
        }

        public List<WhereProperty> WhereProperties
        {
            get
            {
                return this.whereProperties;
            }
            set
            {
                this.whereProperties = value;
            }
        }

    }
    public class WhereProperty
    {
        // Fields
        private string property;
        private LogicalOperators? propertyOperator;

        // Methods
        protected string GetOperator()
        {
            StringBuilder builder = new StringBuilder();
            LogicalOperators valueOrDefault = this.propertyOperator.GetValueOrDefault();
            if (this.propertyOperator.HasValue)
            {
                switch (valueOrDefault)
                {
                    case LogicalOperators.OR:
                        builder.Append(" OR ");
                        break;

                    case LogicalOperators.AND:
                        builder.Append(" AND ");
                        break;

                    case LogicalOperators.ANDNOT:
                        builder.Append(" AND NOT ");
                        break;

                    case LogicalOperators.ORNOT:
                        builder.Append(" OR NOT ");
                        break;
                }
            }
            return builder.ToString();
        }

        public override string ToString()
        {
            return "NotImplemented";
        }

        public string ToString(bool IncludeOperator)
        {
            return (this.GetOperator() + this.ToString());
        }

        // Properties
        public string Property
        {
            get
            {
                return this.property;
            }
            set
            {
                this.property = value;
            }
        }

        public LogicalOperators? PropertyOperator
        {
            get
            {
                return this.propertyOperator;
            }
            set
            {
                this.propertyOperator = value;
            }
        }

    }

    public class WhereMembershipContains : WhereProperty
    {
        [XmlAttribute]
        public bool IgnoreForSiteAdmin { get; set; }
    }

    public class WherePropertyContains : WhereProperty
    {
        // Fields
        private string searchTerm;
        private bool wildcardSearch = false;

        // Methods
        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();

            if (base.Property.Equals("IsDocument", StringComparison.InvariantCultureIgnoreCase))
            {
                builder.Append("IsDocument=");
                builder.Append(this.searchTerm);
            }
            else
            {
                builder.Append("CONTAINS (");
                builder.Append(base.Property);
                builder.Append(",'\"");
                builder.Append(this.searchTerm);
                if (this.wildcardSearch)
                {
                    builder.Append("*");
                }
                builder.Append("\"')");
            }

            return builder.ToString();
        }

        // Properties
        public string SearchTerm
        {
            get
            {
                return this.searchTerm;
            }
            set
            {
                this.searchTerm = value;
            }
        }

        public bool WildcardSearch
        {
            get
            {
                return this.wildcardSearch;
            }
            set
            {
                this.wildcardSearch = value;
            }
        }

    }

    public class WhereStatement
    {
        // Fields
        private string keywords;
        private SearchTypes keywordsType = SearchTypes.Contains;
        private List<string> scopes;
        private List<WhereGroup> whereGroups = new List<WhereGroup>();

        // Methods
        private string GetKeywordStatement()
        {
            StringBuilder builder = new StringBuilder();
            switch (this.KeywordsType)
            {
                case SearchTypes.Contains:
                    builder.Append("CONTAINS ('\"");
                    builder.Append(this.Keywords);
                    builder.Append("\"')");
                    break;

                case SearchTypes.FreeText:
                    builder.Append("FREETEXT(DefaultProperties, '");
                    builder.Append(this.Keywords);
                    builder.Append("')");
                    break;
            }
            return builder.ToString();
        }

        private string GetScopeStatement()
        {
            #region This Site Case
            string thisSite = "This Site";

            foreach (string str in this.Scopes)
            {
                //MJM Custom 'This Site' scope is ignored and appended later as Site=''
                if (str.Trim().Equals(thisSite, StringComparison.InvariantCultureIgnoreCase))
                {
                    thisSite = str;
                    ThisSiteOnly = true;
                    break;
                }
            }

            if (ThisSiteOnly) this.Scopes.Remove(thisSite);
            #endregion

            int count = this.scopes.Count;

            if (count < 1)
            {
                return string.Empty;
            }


            StringBuilder builder = new StringBuilder();
            builder.Append("(");

            foreach (string str in this.Scopes)
            {
                builder.Append(" (\"SCOPE\" = '");
                builder.Append(str);
                builder.Append("') OR");
            }

            builder.Remove(builder.Length - 2, 2);
            builder.Append(") ");
            return builder.ToString();
        }

        private bool ThisSiteOnly = false;

        public string ToString(SPWeb web)
        {
            StringBuilder builder = new StringBuilder();
            if (((this.whereGroups.Count != 0) || !string.IsNullOrEmpty(this.keywords)) || (this.scopes.Count != 0))
            {
                builder.Append("WHERE ");
                builder.Append(this.GetScopeStatement());
                if (!(string.IsNullOrEmpty(this.GetScopeStatement()) || string.IsNullOrEmpty(this.Keywords)))
                {
                    builder.Append(" AND ");
                }
                if (!string.IsNullOrEmpty(this.Keywords))
                {
                    builder.Append(this.GetKeywordStatement());
                }
                if (!(string.IsNullOrEmpty(this.Keywords) || (this.whereGroups.Count <= 0)))
                {
                    builder.Append(" AND ");
                }
                foreach (WhereGroup group in this.whereGroups)
                {
                    if (!group.LogOperator.HasValue)
                    {
                        builder.Append(group.ToString());
                    }
                    else
                    {
                        builder.Append(group.ToString(true));
                    }
                }
                if (this.ThisSiteOnly)
                {
                    builder.Append(" AND Site='");

                    //TODO: Take SPContext out of this class
                    builder.Append(web.Url);

                    builder.Append("' ");
                }
            }
            return builder.ToString();
        }

        // Properties
        public string Keywords
        {
            get
            {
                return this.keywords;
            }
            set
            {
                this.keywords = value;
            }
        }

        public SearchTypes KeywordsType
        {
            get
            {
                return this.keywordsType;
            }
            set
            {
                this.keywordsType = value;
            }
        }

        public List<string> Scopes
        {
            get
            {
                return this.scopes;
            }
            set
            {
                this.scopes = value;
            }
        }

        public List<WhereGroup> WhereGroups
        {
            get
            {
                return this.whereGroups;
            }
            set
            {
                this.whereGroups = value;
            }
        }

    }
}

